package schoolmath.web.components.content;

import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.tapestry5.SelectModel;
import org.apache.tapestry5.annotations.InjectComponent;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.components.Form;
import org.apache.tapestry5.corelib.components.Zone;
import org.apache.tapestry5.hibernate.annotations.CommitAfter;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import schoolmath.web.entities.Role;
import schoolmath.web.entities.User;
import schoolmath.web.services.RoleService;
import schoolmath.web.services.SelectService;
import schoolmath.web.services.valueEncoders.UserEncoder;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@RequiresRoles("Администратор")
public class ManageUsersContent {

	private static Logger logger = LoggerFactory.getLogger(ManageUsersContent.class);
	private List<User> users;
	private List<Role> roles;
	private SelectModel rolesSelectModel;
	private User user;
	@Inject
	private Session session;
	@InjectComponent
	private Form usersEdit;
	@InjectComponent
	private Zone usersEditZone;
	private List<User> deletedUsers = new ArrayList<>();
	private boolean inFormSubmission;
	private List<User> usersSubmitted;
	@Inject
	private SelectService selectService;
	private boolean editing;
	@InjectComponent
	private Zone rowZone;
	@Property
	@Inject
	private UserEncoder userEncoder;
	@Inject
	private RoleService roleService;

	public boolean getCreating() {
		return this.user.getId() == null;
	}

	public boolean getChanging() {
		return editing || this.user.getId() == null;
	}

	public String getChangingId() {
		if (editing) {
			return user.getId().toString();
		} else {
			return "";
		}
	}

	@SuppressWarnings("unchecked")
	public List<User> getUsers() {
		if (users == null) {
			users = session.createCriteria(User.class)
					.addOrder(org.hibernate.criterion.Order.asc("id")).list();
		}
		return this.users;
	}

	@SuppressWarnings("unchecked")
	public List<Role> getRoles() {
		if (roles == null) {
			roles = session.createCriteria(Role.class)
					.addOrder(org.hibernate.criterion.Order.asc("id")).list();
		}
		return roles;
	}

	public SelectModel getRolesSelectModel() {
		if (rolesSelectModel == null) {
			rolesSelectModel = selectService.createSelectModel(selectService.createOptionModelList(getRoles()));
		}
		return rolesSelectModel;
	}

	public void onActivate() {
		inFormSubmission = false;
	}

	public User onAddRow() {
		return new User("", roleService.getRole("Студент"));
	}

	public void onRemoveRow(User user) {
		deletedUsers.add(user);
	}

	public User getUser() {
		return this.user;
	}

	public void setUser(User user) {
		this.user = user;

		if (inFormSubmission) {
			usersSubmitted.add(user);
		}
	}

	@CommitAfter
	private void delete() {
		for (User user : deletedUsers) {
			usersSubmitted.remove(user);
		}
	}

	@CommitAfter
	private void save() {
		for (User user : deletedUsers) {
			if (user.getId() != null) {
				session.delete(user);
			}
		}
		for (User user : usersSubmitted) {
			if (user.getId() != null) {
				session.update(user);
			} else {
				session.merge(user);
			}
		}
		deletedUsers.clear();
	}

	public void onPrepareForSubmit() {
		inFormSubmission = true;
		usersSubmitted = new ArrayList<>();
	}

	public void onValidateFromUsersEdit() {
		Set<String> logins = new HashSet<>();
		for (User user : usersSubmitted) {
			if (logins.contains(user.getLogin())) {
				usersEdit.recordError("Имеются повторяющиеся логины");
				return;
			}
			logins.add(user.getLogin());
		}
	}

	public Object onSuccess() {
		delete();
		save();
		users = null;
		return usersEditZone.getBody();
	}

	public Object onFailure() {
		users = new ArrayList<>(usersSubmitted);
		return usersEditZone.getBody();
	}

	public Object onRefresh() {
		usersEdit.clearErrors();
		users = null;
		return usersEditZone.getBody();
	}

	public Object onEdit(User user) {
		this.user = user;
		editing = true;
		return rowZone.getBody();
	}

	public Object onCancel(User user) {
		this.user = user;
		return rowZone.getBody();
	}

	public Object onRefreshPage() {
		return this;
	}

}
